package cn.zhxu.toys.cipher;

import cn.zhxu.toys.util.DigestUtils;
import org.apache.commons.codec.binary.Base64;

import java.nio.charset.StandardCharsets;

/**
 * 简单解密算法
 * @author Troy.Zhou
 */
public class SimpleDecryptor implements Decryptor {

	protected byte[] secret;
	
	public SimpleDecryptor() {
	}
	
	public SimpleDecryptor(byte[] secret) {
		doInit(secret);
	}
	
	public SimpleDecryptor(String secret) {
		init(secret);
	}

	@Override
	public void init(String secret) {
		doInit(secret.getBytes(StandardCharsets.UTF_8));
	}
	
	protected void doInit(byte[] secret) {
		this.secret = DigestUtils.toMd5(secret);
	}

	@Override
	public byte[] decrypt(byte[] cipherBytes) {
		return doDecrypt(secret, cipherBytes);
	}

	@Override
	public byte[] decrypt(byte[] secretBytes, byte[] cipherBytes) {
		secretBytes = DigestUtils.toMd5(secretBytes);
		return doDecrypt(secretBytes, cipherBytes);
	}
	
	@Override
	public String decrypt(String cipherText) {
		byte[] cipherBytes = Base64.decodeBase64(cipherText);
		byte[] textBytes = doDecrypt(secret, cipherBytes);
		return new String(textBytes, StandardCharsets.UTF_8);
	}

	@Override
	public String decrypt(String secret, String cipherText) {
		byte[] cipherBytes = Base64.decodeBase64(cipherText);
		byte[] textBytes = decrypt(secret.getBytes(StandardCharsets.UTF_8), cipherBytes);
		return new String(textBytes, StandardCharsets.UTF_8);
	}

	protected byte[] doDecrypt(byte[] secretBytes, byte[] cipherBytes) {
		try {
			byte sumy = 0;
			for (byte b : secretBytes) {
				sumy = (byte) ((sumy + b) % 256);
			}
			int textLength = cipherBytes.length - 1;
			byte f = cipherBytes[textLength];
			byte sumx = (byte) ((f - sumy) % 256);
			
			byte[] sBytes = new byte[textLength];
			for (int i = 0; i < textLength; i++) {
				int si = i % secretBytes.length;
				sBytes[i] = (byte) ((secretBytes[si] + sumx) % 256);
			}
			
			byte[] textBytes = new byte[textLength];
			for (int i = 0; i < textLength; i++) {
				textBytes[i] = (byte) ((cipherBytes[i] - sBytes[i]) % 256);
			}
			return textBytes;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
}
